namespace Plugins.Library
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Security;
    using System.Text;
    using Plugins.Interfaces;

    public class PluginLog : IPluginLog
    {
        private readonly string _name;
        private static PluginLog _default = new PluginLog("Plugins.Library");

        public static IPluginLog Default { get { return _default; } }

        private static int messageIndex;
        private static int errorIndex;

        private static long _tics = DateTime.Now.Ticks;
        private static long _current;
        public static void Update()
        {
            _current = DateTime.Now.Ticks;
        }
        public static TimeSpan Elapsed
        {
            get
            {
                return new TimeSpan(_current - _tics);
            }
        }

        public static DateTime Current 
        {
            get 
            {
                return new DateTime(_current);
            }
        }

        internal PluginLog(string name)
        {
            this._name = name;
        }

        private static void Recurse(Exception ex, Action<Exception> action)
        {
            Exception tmp = ex;
            while (tmp != null)
            {
                action(tmp);
                tmp = tmp.InnerException;
            }
        }

        private static string FormatException(Exception ex, Assembly assembly, string source)
        {
            StringBuilder text = new StringBuilder();
            text.AppendLine("error");
            text.AppendLine(assembly.FullName);
            text.AppendLine(source);
            Recurse(ex, e => text.AppendLine(e.Message));
            Recurse(ex, e => text.AppendLine(e.Source));
            Recurse(ex, e => text.AppendLine(e.StackTrace));
            return text.ToString();
        }

        public static void Write(Exception ex, IPluginIdentity obj)
        {
            if (ex != null && obj != null)
            {
                Type type = obj.GetType();
                string _source = type.FullName;
                string _message = FormatException(ex, type.Assembly, _source);
                WriteAsync(string.Format(CultureInfo.InvariantCulture, "{1}{0}{2}", Environment.NewLine, ex.GetType(), _message));
            }
        }

        public void Write(string source, string message)
        {
            this.WriteLogMessage(source, message);
        }

        public static void Write(Exception ex, IPluginIdentity obj, bool defaultLog)
        {
            if (defaultLog)
            {
                Write(ex, obj, _default);
            }
            else
            {
                Write(ex, obj);
            }
        }

        public static void Write(Exception ex, IPluginIdentity obj, IPluginLog log)
        {
            Write(ex, obj, log, PluginProgram.Provider.Messages.Writer);
        }

        public static void Write(Exception ex, IPluginIdentity obj, IPluginLog log, TextWriter writer)
        {
            if (ex != null && obj != null && log != null)
            {
                Type type = obj.GetType();
                string _source = type.FullName;
                string _message = FormatException(ex, type.Assembly, _source);
                log.Write(log.Name, _message);
                WriteAsync(string.Format(CultureInfo.InvariantCulture, "{1}{0}{2}", Environment.NewLine, ex.GetType(), _message), writer, ++messageIndex);
            }
        }

        public static void WriteAsync(string message)
        {
            WriteAsync(message, PluginProgram.Provider.Output.Writer, ++messageIndex);
        }

        public static void WriteAsync(string message, TextWriter writer, int messageIndex)
        {
            _default.ProcessMessage(_default.Name, message);
            PluginActionDelegateWorkItem<string>.QueryAsync(message, delegate(string item)
            {
                Update();
                writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]:[{3}]", messageIndex, Current, Elapsed, item));
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]:[{3}]", messageIndex, Current, Elapsed, item));
            });
        }

        public static void WriteErrorSync(Exception ex)
        {
            WriteErrorSync(ex, PluginProgram.Provider.Exceptions.Writer);
        }

        public static void WriteErrorSync(Exception ex, TextWriter writer)
        {
            if (ex != null)
            {
                Type type = ex.TargetSite.DeclaringType;
                string _source = type.FullName;
                string _message = FormatException(ex, type.Assembly, _source);
                WriteErrorSync(_message, writer, ++errorIndex);
            }
        }

        public static void WriteErrorSync(string message)
        {
            WriteErrorSync(message, PluginProgram.Provider.Exceptions.Writer, ++errorIndex);
        }

        public static void WriteErrorSync(string message, TextWriter writer, int messageIndex)
        {
            PluginActionDelegateWorkItem<string>.QuerySync(message, delegate(string item)
            {
                Update();
                writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]:[{3}]", messageIndex, Current, Elapsed, item));
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]:[{3}]", messageIndex, Current, Elapsed, item));
            });
        }

        private void WriteLogMessage(string source, string message)
        {
            try
            {
                if (!EventLog.SourceExists(source))
                {
                    EventLog.CreateEventSource(source, this._name);
                }
                EventLog log = new EventLog(this._name) { Source = source };
                log.WriteEntry(message, EventLogEntryType.Information);
                log.Close();
            }
            catch (FileLoadException ex)
            {
                WriteErrorSync(ex);
            }
            catch (FileNotFoundException ex)
            {
                WriteErrorSync(ex);
            }
            catch (IOException ex)
            {
                WriteErrorSync(ex);
            }
            catch (ArgumentNullException ex)
            {
                WriteErrorSync(ex);
            }
            catch (SecurityException ex)
            {
                WriteErrorSync(ex);
            }
            catch (AccessViolationException ex)
            {
                WriteErrorSync(ex);
            }
            catch (ApplicationException ex)
            {
                WriteErrorSync(ex);
            }
            catch (FormatException ex)
            {
                WriteErrorSync(ex);
            }
            catch (ArgumentException ex)
            {
                WriteErrorSync(ex);
            }
            catch (UnauthorizedAccessException ex)
            {
                WriteErrorSync(ex);
            }
            catch (InvalidOperationException ex)
            {
                WriteErrorSync(ex);
            }
            catch (Win32Exception ex)
            {
                WriteErrorSync(ex);
            }
        }

        public static void WriteOutSync(Exception ex)
        {
            WriteOutSync(string.Format(CultureInfo.InvariantCulture, "{0}#{0}{1}{0}#{0}{2}{0}#{0}", Environment.NewLine, ex.Message, ex.StackTrace), Console.Out, ++messageIndex);
        }

        public static void WriteOutSync(string message)
        {
            WriteOutSync(message, PluginProgram.Provider.Messages.Writer, ++messageIndex);
        }

        public static void WriteOutSync(string message, TextWriter writer, int messageIndex)
        {
            PluginActionDelegateWorkItem<string>.QuerySync(message, delegate(string item)
            {
                Update();
                _default.ProcessMessage(_default.Name, message);
                writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]:[{3}]", messageIndex, Current, Elapsed, item));
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]:[{3}]", messageIndex, Current, Elapsed, item));
            });
        }

        public static void WriteSync(string message)
        {
            WriteSync(message, PluginProgram.Provider.Output.Writer, ++messageIndex);
        }

        public static void WriteSync(string message, TextWriter writer, int messageIndex)
        {
            _default.ProcessMessage(_default.Name, message);
            PluginActionDelegateWorkItem<string>.QuerySync(message, delegate(string item)
            {
                Update();
                writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]:[{3}]", messageIndex, Current, Elapsed, item));
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0, 6}]:[{1}]:[{2}]:[{3}]", messageIndex, Current, Elapsed, item));
            });
        }

        public string Name
        {
            get
            {
                return this._name;
            }
        }

        #region IEventHandler<string> Members

        private Dictionary<string, IList<Action<string>>> _eventList = new Dictionary<string, IList<Action<string>>>();

        public static void AddHandler(Action<string> propertyHandler)
        {
            if (propertyHandler != null)
            {
                _default.AddEvent(_default._name, propertyHandler);
            }
        }

        public static bool ContainsHandle(Action<string> propertyHandler)
        {
            if (propertyHandler != null)
            {
                return _default.ContainsHandle(_default._name, propertyHandler);
            }
            return false;
        }

        private bool ContainsHandle(string propertyName, Action<string> propertyHandler)
        {
            lock (_sync)
            {
                if (propertyHandler != null)
                {
                    IList<Action<string>> entries = null;
                    if (_eventList.ContainsKey(propertyName))
                    {
                        entries = _eventList[propertyName];
                        if (entries.Contains(propertyHandler))
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
        }

        public static void RemoveHandler(Action<string> propertyHandler)
        {
            if (propertyHandler != null)
            {
                _default.RemoveEvent(_default._name, propertyHandler);
            }
        }

        private static object _sync = new object();

        public void AddEvent(string propertyName, Action<string> propertyHandler, int priority)
        {
            if (propertyName != null && propertyHandler != null)
            {
                AddEvent(propertyName, propertyHandler);
            }
        }

        public void AddEvent(string propertyName, Action<string> propertyHandler)
        {
            lock (_sync)
            {
                if (propertyName != null && propertyHandler != null)
                {
                    IList<Action<string>> entries = null;
                    if (_eventList.ContainsKey(propertyName))
                    {
                        entries = _eventList[propertyName];
                    }
                    else
                    {
                        entries = new List<Action<string>>();
                        entries.Add(propertyHandler);
                        _eventList.Add(propertyName, entries);
                        return;
                    }
                    if (!entries.Contains(propertyHandler))
                    {
                        entries.Add(propertyHandler);
                    }
                }
            }
        }

        public void RemoveEvent(string propertyName, Action<string> propertyHandler, int priority)
        {
            if (propertyName != null && propertyHandler != null)
            {
                RemoveEvent(propertyName, propertyHandler);
            }
        }

        public void RemoveEvent(string propertyName, Action<string> propertyHandler)
        {
            lock (_sync)
            {
                if (propertyName != null && propertyHandler != null)
                {
                    IList<Action<string>> entries = null;
                    if (_eventList.ContainsKey(propertyName))
                    {
                        entries = _eventList[propertyName];
                        if (entries.Contains(propertyHandler))
                        {
                            entries.Remove(propertyHandler);
                        }
                        if (entries.Count == 0)
                        {
                            _eventList.Remove(propertyName);
                        }
                    }
                }
            }
        }

        private void ProcessMessage(string property, string message)
        {
            //lock (this)
            //{
            if (property != null && message != null)
            {
                if (_eventList.ContainsKey(property))
                {
                    IList<Action<string>> actionList = _eventList[property];
                    if (actionList != null)
                    {
                        foreach (Action<string> action in actionList)
                        {
                            action(message);
                        }
                    }
                }
            }
            //}
        }

        #endregion
    }
}

